package cn.izeno.wx.service

import cn.izeno.wx.Cache
import cn.izeno.wx.core.MultiConverterFactory
import cn.izeno.wx.models.web.CookieHolder
import cn.izeno.wx.models.web.WebWxLoginRes
import io.reactivex.rxjava3.core.Observable
import okhttp3.*
import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import java.util.logging.Logger
import javax.net.ssl.SSLContext
import javax.net.ssl.X509TrustManager


/**
 * 微信网页版
 */
object Web {
  private val logger = Logger.getLogger("cn.izeno.wx.service.Web")

  val service: WebWxService

  private var cookie: ArrayList<Cookie> = ArrayList()

  init {
    val retrofit = Retrofit.Builder().baseUrl("https://login.weixin.qq.com/")
        .client(client())
        .addCallAdapterFactory(RxJava3CallAdapterFactory.createSynchronous())
        .addConverterFactory(MultiConverterFactory.create())
        .build()

    service = retrofit.create(WebWxService::class.java)
  }

  fun uuid(): Observable<String> {
    return service.uuid().map {
      Regex("""window.QRLogin.code = (\d+); window.QRLogin.uuid = \"([^"]+)\";""")
          .find(it)?.groups?.get(2)?.value ?: throw IllegalStateException("获取 UUID 失败")
    }
  }


  fun login(uuid: String): Observable<String> {
    return Observable.create<String> { emitter ->
      var loop = true
      while (loop) {
        service.login(uuid).map {
          val res = parseWebWxLoginResult(it)
          when (res.code) {
            0L -> throw IllegalStateException("登录失败")
            400L -> throw IllegalStateException("二维码已过期")
          }
          res
        }.filter { it.code == 200L }.subscribe({
          loop = false
          emitter.onNext(it.redirectUrl)
          emitter.onComplete()
        }, {
          loop = false
          emitter.onError(it)
        })
      }
    }
  }

  private fun client(): OkHttpClient {
    val trustManager = object : X509TrustManager {
      override fun checkClientTrusted(p0: Array<out X509Certificate>?, p1: String?) {}
      override fun checkServerTrusted(p0: Array<out X509Certificate>?, p1: String?) {}
      override fun getAcceptedIssuers(): Array<X509Certificate> {
        return emptyArray()
      }
    }

    val ssl = SSLContext.getInstance("SSL")
    ssl.init(null, arrayOf(trustManager), SecureRandom())

    return OkHttpClient.Builder()
        // 请求
        .readTimeout(1, TimeUnit.MINUTES)
        .followRedirects(false)
        .followSslRedirects(false)
        .addInterceptor { chain ->
          val request = chain.request().newBuilder()
              .removeHeader("User-Agent")
              .addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36")
              .build()

          var res = chain.proceed(request)
          // 获取文件，不处理后续的内容
          if (request.url().toString().contains("webwxget")) {
            res
          } else {
            var content = ""

            res.body()?.use {
              content = it.string().replace("window.synccheck=", "")
//            if (content.length > 100) content = content.substring(0, 100)
//            logger.info("\n[${request.method()}] ${request.url()}")
              res = res.newBuilder().body(ResponseBody.create(null, content)).build()
            }

            if (content.isNotEmpty() && res.code() == 301) {
              res = res.newBuilder().code(200).build()
            }

            res
          }
        }
        .sslSocketFactory(ssl.socketFactory, trustManager)
        .cookieJar(cookie())
        .build()
  }

  /**
   * [OkHttp3 (四)——Cookie与拦截器](https://www.jianshu.com/p/3360f4b6b3fe)
   */
  private fun cookie(): CookieJar {

    return object : CookieJar {

      init {
        val cookieCached = Cache["web_wx_cookie"] as? ArrayList<CookieHolder> ?: ArrayList()

        cookie = cookieCached.map {
          Cookie.Builder()
              .name(it.name)
              .value(it.value)
              .expiresAt(it.expiresAt)
              .domain(it.domain)
              .path(it.path)
              .build()
        } as ArrayList<Cookie>
      }

      override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
        // 去重
        val newsCookieNames = cookies.map { it.name() }.joinToString(",")
        cookie = cookie.filter { newsCookieNames.contains(it.name()) } as ArrayList<Cookie>

        cookie.addAll(cookies)

        Cache["web_wx_cookie"] = cookie.map {
          val holder = CookieHolder()
          holder.name = it.name()
          holder.value = it.value()
          holder.expiresAt = it.expiresAt()
          holder.domain = it.domain()
          holder.path = it.path()
          holder
        } as ArrayList<CookieHolder>
      }

      override fun loadForRequest(url: HttpUrl): List<Cookie> {
        return cookie
      }
    }
  }

  private fun parseWebWxLoginResult(raw: String): WebWxLoginRes {
    val result = WebWxLoginRes()
    raw.replace(Regex("[ '\";\r\n]"), "").split("window.")
        .filter { kv -> kv.isNotEmpty() }.map { item ->
          val kv = item.split('=', limit = 2)
          Pair(kv[0], kv[1])
        }.forEach { kv ->
          when (kv.first) {
            "code" -> result.code = kv.second.toLong()
            "redirect_uri" -> result.redirectUrl = kv.second
            "userAvatar" -> result.avatar = kv.second
          }
        }

    return result
  }


  /**
   * 清除cookie 缓存
   */
  fun clearCookie() {
    cookie.clear()
    Cache["web_wx_cookie"] = ArrayList<CookieHolder>()
  }
}
